﻿namespace HuwmanCode.IO

open System
open System.IO
open System.ComponentModel
open System.Runtime.InteropServices

type TransactedFile() =
    static let translateFileAccess access =
        match access with
        | FileAccess.Read -> NativeFileAccess.GenericRead
        | _ -> NativeFileAccess.GenericWrite

    static let translateFileMode mode = 
        let nativeMode:NativeFileMode = 
            match mode with
            | FileMode.Append -> enum((int)FileMode.OpenOrCreate)
            | m -> enum((int)m)
        nativeMode

    static let translateFileShare (share:FileShare) =
        let nativeShare:NativeFileShare = enum((int)share)
        nativeShare

    /// <summary>
    /// Transaction aware implementation of System.IO.File.Open
    /// </summary>
    /// <param name="path"></param>
    /// <param name="mode"></param>
    /// <param name="access"></param>
    /// <param name="share"></param>
    /// <returns></returns>
    static member Open(path, mode, access, share) =
        use handle = KtmTransactionHandle.Create()
        let fileHandle = CreateFileTransacted(path, translateFileAccess access, translateFileShare share, IntPtr.Zero, translateFileMode mode, 0, IntPtr.Zero, handle, IntPtr.Zero, IntPtr.Zero)
        if fileHandle.IsInvalid then raise(Win32Exception(Marshal.GetLastWin32Error()))

        new FileStream(fileHandle, access)

    /// <summary>
    /// Reads all text from a file as part of a transaction
    /// </summary>
    /// <param name="path"></param>
    /// <param name="contents"></param>
    /// <returns></returns>
    static member ReadAllText(path) =
        use reader = new StreamReader(TransactedFile.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
        reader.ReadToEnd()

    /// <summary>
    /// Writes text to a file as part of a transaction
    /// </summary>
    /// <param name="path"></param>
    /// <param name="contents"></param>
    static member WriteAllText(path, contents:string) =
        use writer = new StreamWriter(TransactedFile.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
        writer.Write(contents)

    /// <summary>
    /// Deletes a file as part of a transaction
    /// </summary>
    /// <param name="file"></param>
    static member Delete(file) =
        use handle = KtmTransactionHandle.Create()
        if not <| DeleteFileTransacted(file, handle) then raise(Win32Exception(Marshal.GetLastWin32Error()))//raise(Exception("Unable to perform transacted file delete."))